package com.xy.job.admin.core.notify

import com.dingtalk.api.DefaultDingTalkClient
import com.dingtalk.api.DingTalkClient
import com.dingtalk.api.request.OapiRobotSendRequest
import com.xy.job.admin.domain.NotifyChannelConfig
import com.xy.job.admin.domain.NotifyInstance
import com.xy.job.admin.enums.NotifyChannelType
import com.xy.job.admin.enums.NotifyStatus
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import org.slf4j.Logger
import org.slf4j.LoggerFactory
//import org.springframework.beans.factory.annotation.Value
//import org.springframework.mail.MailSender
//import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Component
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.reflect.full.starProjectedType


sealed class NotifierSenders {

    abstract class BaseNotifySender<Context : NotifyContext, Config : NotifyChannel> : INotifySender {

        protected val log: Logger = LoggerFactory.getLogger(this::class.java)

        protected abstract val notifyChannelType: NotifyChannelType

        @Suppress("UNCHECKED_CAST")
        override fun notify(notifyInstance: NotifyInstance, notifyChannelConfig: NotifyChannelConfig): NotifyStatus {
            try {
                val context = Json.decodeFromString(
                    deserializer = serializer(notifyChannelType.notifyContentClass.starProjectedType),
                    string = notifyChannelConfig.content!!
                ) as Context
                val config = Json.decodeFromString(
                    deserializer = serializer(notifyChannelType.channelConfigClass.starProjectedType),
                    string = notifyChannelConfig.config!!
                ) as Config
                return notify(context, config)
            } catch (e: Throwable) {
                log.error("notify failed, instanceId=${notifyChannelConfig.id}, message=${e.message}", e)
                return NotifyStatus.FAIL
            }
        }

        abstract fun notify(context: Context, config: Config): NotifyStatus
    }

    @Component
    class DingTalkRobot : BaseNotifySender<NotifyContext.DingTalkRobot, NotifyChannel.DingTalkRobot>() {

        override val notifyChannelType: NotifyChannelType = NotifyChannelType.DING_TALK_ROBOT

        val api: String = "https://oapi.dingtalk.com/robot/send?sign=%s&timestamp=%s"

        override fun notify(
            context: NotifyContext.DingTalkRobot,
            config: NotifyChannel.DingTalkRobot
        ): NotifyStatus {
            val timestamp = System.currentTimeMillis()
            val sign = generateSign(timestamp, config.secret!!)
            val client: DingTalkClient = DefaultDingTalkClient(api.format(sign, timestamp))
            val req = buildRequest(context.content!!)

            val response = client.execute(req, config.token!!)
            if (!response.isSuccess) {
                log.error(
                    "send DingTalk message failed: errorCode={}, errorMessage={}",
                    response.errcode, response.errmsg
                )
                return NotifyStatus.FAIL
            }
            return NotifyStatus.SUCCESS
        }

        private fun buildRequest(content: String): OapiRobotSendRequest {
            return OapiRobotSendRequest().apply {
                msgtype = "text"
                val text = OapiRobotSendRequest.Text().apply {
                    this.content = content
                }
                setText(text)
            }
        }

        private fun generateSign(timestamp: Long, secret: String): String {
            val stringToSign = """
            $timestamp
            $secret
            """.trimIndent()
            val mac = Mac.getInstance("HmacSHA256")
            mac.init(SecretKeySpec(secret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256"))
            val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
            return URLEncoder.encode(String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8)
        }
    }

//    @Component("emailSender")
//    class Email(
//        var mailSender: MailSender,
//        @Value("\${spring.mail.username}") var mailAccount: String,
//    ) : BaseNotifySender<NotifyContext.Email, NotifyChannel.Email>() {
//
//        override val notifyChannelType: NotifyChannelType = NotifyChannelType.Email
//
//        override fun notify(context: NotifyContext.Email, config: NotifyChannel.Email): NotifyStatus {
//            val message = SimpleMailMessage().apply {
//                this.setTo(*config.receivers!!.toTypedArray())
//                this.from = mailAccount
//                this.subject = context.title
//                this.text = context.content
//            }
//            mailSender.send(message)
//            return NotifyStatus.SUCCESS
//        }
//    }
}
